/**@file
 *  
 * Implement for MMIO related functions. 
 *  
 * @author : Lu, Ken (bluewish.ken.lu@live.cn) 
 */

#include "internal.h"

#define MAX_MMIO_MAP_LENGTH  0x100000


/**
 * IO read 
 * 
 * @author Ken (2012/4/20)
 * 
 * @param port 
 * @param width 
 * @param output 
 * @param output_length 
 * 
 * @return NTSTATUS 
 */
NTSTATUS
KSIoRead (
    u32_t port,
    u32_t width,
    u8_t  *output,
    u32_t *output_length
    )
{
    NTSTATUS status = STATUS_SUCCESS;

    k_func_enter();
    k_debug("output_length=0x%x, width=0x%x", *output_length, width);
    if (*output_length < width) {
        status = STATUS_INVALID_PARAMETER;
        goto exit;
    }
    switch (width) {
    case 1:
        *output = READ_PORT_UCHAR((PUCHAR)port);
        break;
    case 2:
        *(u16_t*)output = READ_PORT_USHORT((PUSHORT)port);
        break;
    case 4:
        *(u32_t*)output = READ_PORT_ULONG((PULONG)port);
        break;
    }
    *output_length = width;
    k_func_leave();
exit:
    return status;
}

/**
 * IO Write
 * 
 * @author Ken (2012/4/20)
 * 
 * @param port 
 * @param width 
 * @param value 
 * 
 * @return NTSTATUS 
 */
NTSTATUS
KSIoWrite (
    u32_t port,
    u32_t width,
    u32_t value
    )
{
    NTSTATUS status = STATUS_SUCCESS;

    k_func_enter();
    switch (width) {
    case 1:
        WRITE_PORT_UCHAR((PUCHAR)port, (UCHAR)value);
        break;
    case 2:
        WRITE_PORT_USHORT((PUSHORT)port, (USHORT)value);
        break;
    case 4:
        WRITE_PORT_ULONG((PULONG)port, (ULONG)value);
        break;
    }
    k_func_leave();
    return status;
}

/**
 * Send read byte command to IO port
 * 
 * @author Ken (2012/4/20)
 * 
 * @param index_port 
 * @param data_port 
 * @param index 
 * 
 * @return u8_t 
 */
u8_t 
KSIoPortReadByte(
    u32_t index_port,
    u32_t data_port,
    u8_t  index
    )
{
    u8_t value;
    _asm {
        cli
        mov dx, word ptr[index_port]
        mov al, byte ptr[index]
        out dx, al
        nop
        nop
        nop
        mov dx, word ptr[data_port]
        in  al, dx
        mov value, al
        sti
    }
    return value;
}

/**
 * Read 256 bytes by given index port.
 * 
 * @author Ken (2012/4/17)
 * 
 * @param index_port 
 * @param data_port 
 * @param buffer 
 * 
 * @return NTSTATUS 
 */
NTSTATUS
KSIoPortRead(
    u32_t index_port,
    u32_t data_port,
    u8_t* buffer
    )
{
    NTSTATUS status = STATUS_SUCCESS;
    u8_t index;
    
    k_func_enter();
    __try
    {
        for (index = 0; index < 0xFF; index++) {
            buffer[index] = KSIoPortReadByte(index_port, data_port, index);
        }
    } __except (EXCEPTION_EXECUTE_HANDLER) {
          status = STATUS_ACCESS_VIOLATION;
          k_debug("Fail to read IO port (0x%x:0x%x)", index_port, data_port);
    }

    k_func_leave();
    return status;
}
